package java_thinking.unit_21._21_7;

import java.util.*;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Desc:   7-21 在 ExecutorService 中跟踪在关闭之后被取消的任务
 *          1.要想这项技术能发挥作用，任务在返回时必须维持线程的中断状态，
 *            在所有设计良好的任务中都会实现这个功能
 * @author: cww
 * @DateTime: 2019-06-16 17:36
 */

public class TrackingExecutor extends AbstractExecutorService {
    private final ExecutorService exec;
    private final Set<Runnable> tasksCancelledAtShutdown = Collections.synchronizedSet(new HashSet<Runnable>());

    public TrackingExecutor(ExecutorService exec) {
        this.exec = exec;
    }
    @Override
    public void shutdown() {
        exec.shutdown();
    }

    @Override
    public List<Runnable> shutdownNow() {
        return exec.shutdownNow();
    }

    @Override
    public boolean isShutdown() {
        return exec.isShutdown();
    }

    @Override
    public boolean isTerminated() {
        return exec.isTerminated();
    }

    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit)
            throws InterruptedException {
        return exec.awaitTermination(timeout, unit);
    }

    public List<Runnable> getCancelledTask() {
        if (!exec.isTerminated()) {
            throw new IllegalStateException("线程还没关闭完成");
        }
        return new ArrayList<>(tasksCancelledAtShutdown);
    }

    @Override
    public void execute(final Runnable runnable) {
        exec.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    runnable.run();
                } finally {
                    /**
                     * 每个线程运行结束(不管是否正常完成)都进行判断是否发起关闭并且是否是中断的
                     * 但是这里存在一个可能误报的问题：一些被认为已经取消的任务实际上已经执行完成，原因是：
                     * 在任务执行最后一条指令以及线程池将任务记录为结束的两个时刻之间，线程池可能被关闭，
                     * 如果任务是幂等的(Idempotent,即任务执行两次与执行一次会得到相同的结果)，那么这不会存在问题，
                     * 在WebCrawler 中这个情况是幂等的，所以可以允许，但是其他程序就要小心做好准备
                     */
                    if (isShutdown() && Thread.currentThread().isInterrupted())
                        tasksCancelledAtShutdown.add(runnable);
                }
            }
        });
    }
}
